package com.example.jwt.filter.security;

import cn.hutool.core.util.StrUtil;
import com.example.jwt.entity.security.JwtAuthenticationToken;
import com.example.jwt.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 登陆成功之后, 访问受保护资源都需要携带 token 进行访问, 此过滤器对携带的 token 进行合法性校验
 */
@Slf4j
public class JwtTokenFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;

    public JwtTokenFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authorization = request.getHeader("Authorization");
        if (authorization == null) {
            // 未登录, 直接通行, 让 AnonymousAuthenticationFilter 给个匿名用户, 允许访问没有限制角色的资源
            filterChain.doFilter(request, response);
            return;

//            authenticationTokenFailureHandler(response, "未登录, 请先登陆后再访问");
//            return;
        }

        String token = StrUtil.removePrefix(authorization, "Bearer").trim();

        try {
            if (!JwtTokenUtil.validateToken(token)) {
                authenticationTokenFailureHandler(response, "登陆凭证失效, 请重新登陆");
                return;
            }

            // token 校验通过, 从数据库获得用户, 放到 SecurityContextHolder
            UserDetails userDetails = userDetailsService.loadUserByUsername(JwtTokenUtil.getSubjectFromToken(token));
            JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(userDetails, "", userDetails.getAuthorities());
            // 设置当前的 AuthenticationToken 为 已经通过验证，不然后面在过滤连的最后一个 FilterSecurityInterceptor#invoke 的 beforeInvocation 还会对这个 AuthenticationToken 使用 Provider 再次进行校验
            // 若是这里不设为 true， 也可以把 上面构建 token 时 credentials 参数填入 request 中的 password 参数值， 用来通过 Provider 校验
            jwtAuthenticationToken.setAuthenticated(true);
            jwtAuthenticationToken.setDetails(new WebAuthenticationDetails(request));
            // 将当前 AuthenticationToken 保存到 SecurityContextHolder
            SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            log.error("校验 token 出错, 原因 --> {}", e.getCause().getMessage());
            authenticationTokenFailureHandler(response, "登陆凭证失效, 请重新登陆");
        }

    }

    private void authenticationTokenFailureHandler(HttpServletResponse response, String message) {
        response.setHeader("Content-Type", "application/json;charset=utf-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.write(message);
            out.flush();
        } catch (IOException e) {
            log.error("response.getWriter() 出现异常, 原因  --> {}", e.getCause().getMessage());
        }

    }
}
